//! RK3588 MIPI DSI Controller Driver
//! 
//! This module provides a driver for the RK3588 MIPI DSI (Display Serial Interface) controller.
//! 
//! # Hardware Specifications
//! - RK3588 has 2 MIPI DSI controllers (DSI0 and DSI1)
//! - Each supports up to 4 data lanes
//! - Maximum data rate: 2.5 Gbps per lane
//! - Supports video mode and command mode
//! - Resolution support: up to 4K
//! 
//! # Memory Map
//! - DSI0: 0xFDE2_0000
//! - DSI1: 0xFDE3_0000

use core::ptr::{read_volatile, write_volatile};

/// MIPI DSI Register Offsets
const DSI_VERSION: usize = 0x000;
const DSI_PWR_UP: usize = 0x004;
const DSI_CLKMGR_CFG: usize = 0x008;
const DSI_DPI_VCID: usize = 0x00C;
const DSI_DPI_COLOR_CODING: usize = 0x010;
const DSI_DPI_CFG_POL: usize = 0x014;
const DSI_DPI_LP_CMD_TIM: usize = 0x018;
const DSI_PCKHDL_CFG: usize = 0x02C;
const DSI_GEN_VCID: usize = 0x030;
const DSI_MODE_CFG: usize = 0x034;
const DSI_VID_MODE_CFG: usize = 0x038;
const DSI_VID_PKT_SIZE: usize = 0x03C;
const DSI_VID_NUM_CHUNKS: usize = 0x040;
const DSI_VID_NULL_SIZE: usize = 0x044;
const DSI_VID_HSA_TIME: usize = 0x048;
const DSI_VID_HBP_TIME: usize = 0x04C;
const DSI_VID_HLINE_TIME: usize = 0x050;
const DSI_VID_VSA_LINES: usize = 0x054;
const DSI_VID_VBP_LINES: usize = 0x058;
const DSI_VID_VFP_LINES: usize = 0x05C;
const DSI_VID_VACTIVE_LINES: usize = 0x060;
const DSI_CMD_MODE_CFG: usize = 0x068;
const DSI_GEN_HDR: usize = 0x06C;
const DSI_GEN_PLD_DATA: usize = 0x070;
const DSI_CMD_PKT_STATUS: usize = 0x074;
const DSI_TO_CNT_CFG: usize = 0x078;
const DSI_PHY_RSTZ: usize = 0x0A0;
const DSI_PHY_IF_CFG: usize = 0x0A4;
const DSI_PHY_STATUS: usize = 0x0B0;
const DSI_PHY_TST_CTRL0: usize = 0x0B4;
const DSI_PHY_TST_CTRL1: usize = 0x0B8;
const DSI_INT_ST0: usize = 0x0BC;
const DSI_INT_ST1: usize = 0x0C0;

/// DSI Power Control
const DSI_PWR_UP_POWERUP: u32 = 1 << 0;
const DSI_PWR_UP_RESET: u32 = 0;

/// DSI PHY Control
const DSI_PHY_RSTZ_SHUTDOWNZ: u32 = 1 << 0;
const DSI_PHY_RSTZ_RSTZ: u32 = 1 << 1;
const DSI_PHY_RSTZ_ENABLECLK: u32 = 1 << 2;

/// DSI Mode Configuration
const DSI_MODE_CFG_CMD_MODE: u32 = 1 << 0;
const DSI_MODE_CFG_VIDEO_MODE: u32 = 0 << 0;

/// DSI Video Mode Types
const DSI_VID_MODE_TYPE_NON_BURST_SYNC_PULSES: u32 = 0;
const DSI_VID_MODE_TYPE_NON_BURST_SYNC_EVENTS: u32 = 1;
const DSI_VID_MODE_TYPE_BURST: u32 = 2;

/// MIPI DSI Controller Selection
#[derive(Debug, Clone, Copy)]
pub enum DsiController {
    DSI0 = 0xFDE2_0000,
    DSI1 = 0xFDE3_0000,
}

/// MIPI DSI Color Format
#[derive(Debug, Clone, Copy)]
pub enum DsiColorFormat {
    Rgb565 = 0,
    Rgb666Packed = 1,
    Rgb666Loosely = 2,
    Rgb888 = 3,
}

/// MIPI DSI Video Mode
#[derive(Debug, Clone, Copy)]
pub enum DsiVideoMode {
    NonBurstSyncPulses,
    NonBurstSyncEvents,
    Burst,
}

/// MIPI DSI PHY Configuration
#[derive(Debug, Clone, Copy)]
pub struct DsiPhyConfig {
    pub lanes: u8,           // Number of data lanes (1-4)
    pub hs_clk_freq_mhz: u32, // High-speed clock frequency in MHz
}

/// Display Timing Configuration
#[derive(Debug, Clone, Copy)]
pub struct DisplayTiming {
    pub width: u16,          // Horizontal active pixels
    pub height: u16,         // Vertical active lines
    pub hsa: u16,            // Horizontal sync active
    pub hbp: u16,            // Horizontal back porch
    pub hfp: u16,            // Horizontal front porch
    pub vsa: u16,            // Vertical sync active
    pub vbp: u16,            // Vertical back porch
    pub vfp: u16,            // Vertical front porch
}

/// MIPI DSI Error Types
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DsiError {
    Timeout,
    PhyNotReady,
    InvalidParameter,
    CommandFailed,
}

/// RK3588 MIPI DSI Driver
pub struct Rk3588MipiDsi {
    base_addr: usize,
    phy_config: DsiPhyConfig,
    color_format: DsiColorFormat,
}

impl Rk3588MipiDsi {
    /// Create a new MIPI DSI controller instance
    pub fn new(
        controller: DsiController,
        phy_config: DsiPhyConfig,
        color_format: DsiColorFormat,
    ) -> Self {
        Self {
            base_addr: controller as usize,
            phy_config,
            color_format,
        }
    }

    /// Write to register
    #[inline]
    fn write_reg(&self, offset: usize, value: u32) {
        unsafe {
            write_volatile((self.base_addr + offset) as *mut u32, value);
        }
    }

    /// Read from register
    #[inline]
    fn read_reg(&self, offset: usize) -> u32 {
        unsafe { read_volatile((self.base_addr + offset) as *const u32) }
    }

    /// Initialize the MIPI DSI controller
    pub fn init(&mut self, timing: &DisplayTiming) -> Result<(), DsiError> {
        // Power down DSI
        self.write_reg(DSI_PWR_UP, DSI_PWR_UP_RESET);

        // Configure PHY
        self.configure_phy()?;

        // Configure DPI (Display Pixel Interface)
        self.configure_dpi(timing)?;

        // Configure video mode
        self.configure_video_mode(timing)?;

        // Configure command mode
        self.configure_command_mode();

        // Power up DSI
        self.write_reg(DSI_PWR_UP, DSI_PWR_UP_POWERUP);

        // Wait for PHY to be ready
        self.wait_phy_ready()?;

        Ok(())
    }

    /// Configure PHY
    fn configure_phy(&mut self) -> Result<(), DsiError> {
        if self.phy_config.lanes == 0 || self.phy_config.lanes > 4 {
            return Err(DsiError::InvalidParameter);
        }

        // Configure number of lanes
        self.write_reg(DSI_PHY_IF_CFG, (self.phy_config.lanes - 1) as u32);

        // Configure clock lane timing
        // These values should be calculated based on hs_clk_freq_mhz
        // For now, using generic safe values
        let tx_esc_clk_division = 0x04;
        self.write_reg(DSI_CLKMGR_CFG, tx_esc_clk_division);

        // Reset PHY
        self.write_reg(DSI_PHY_RSTZ, 0);

        // Enable PHY
        self.write_reg(
            DSI_PHY_RSTZ,
            DSI_PHY_RSTZ_SHUTDOWNZ | DSI_PHY_RSTZ_RSTZ | DSI_PHY_RSTZ_ENABLECLK,
        );

        Ok(())
    }

    /// Configure DPI (Display Pixel Interface)
    fn configure_dpi(&mut self, _timing: &DisplayTiming) -> Result<(), DsiError> {
        // Set virtual channel ID to 0
        self.write_reg(DSI_DPI_VCID, 0);

        // Set color coding based on format
        let color_coding = match self.color_format {
            DsiColorFormat::Rgb565 => 0,
            DsiColorFormat::Rgb666Packed => 1,
            DsiColorFormat::Rgb666Loosely => 2,
            DsiColorFormat::Rgb888 => 5,
        };
        self.write_reg(DSI_DPI_COLOR_CODING, color_coding);

        // Configure polarity (active high for all signals)
        self.write_reg(DSI_DPI_CFG_POL, 0);

        Ok(())
    }

    /// Configure video mode
    fn configure_video_mode(&mut self, timing: &DisplayTiming) -> Result<(), DsiError> {
        // Set video mode type to burst mode
        self.write_reg(DSI_VID_MODE_CFG, DSI_VID_MODE_TYPE_BURST);

        // Set packet size (horizontal active pixels)
        self.write_reg(DSI_VID_PKT_SIZE, timing.width as u32);

        // Set number of chunks to 0 (single packet per line)
        self.write_reg(DSI_VID_NUM_CHUNKS, 0);

        // Set null packet size to 0
        self.write_reg(DSI_VID_NULL_SIZE, 0);

        // Set horizontal timing
        self.write_reg(DSI_VID_HSA_TIME, timing.hsa as u32);
        self.write_reg(DSI_VID_HBP_TIME, timing.hbp as u32);
        let hline_time = timing.width + timing.hsa + timing.hbp + timing.hfp;
        self.write_reg(DSI_VID_HLINE_TIME, hline_time as u32);

        // Set vertical timing
        self.write_reg(DSI_VID_VSA_LINES, timing.vsa as u32);
        self.write_reg(DSI_VID_VBP_LINES, timing.vbp as u32);
        self.write_reg(DSI_VID_VFP_LINES, timing.vfp as u32);
        self.write_reg(DSI_VID_VACTIVE_LINES, timing.height as u32);

        Ok(())
    }

    /// Configure command mode
    fn configure_command_mode(&mut self) {
        // Enable all command types in command mode
        self.write_reg(DSI_CMD_MODE_CFG, 0x0000_0000);

        // Configure timeout counters
        self.write_reg(DSI_TO_CNT_CFG, 0xFFFF_FFFF);
    }

    /// Wait for PHY to be ready
    fn wait_phy_ready(&self) -> Result<(), DsiError> {
        let timeout = 1000000; // 1 second timeout
        let mut count = 0;

        while count < timeout {
            let status = self.read_reg(DSI_PHY_STATUS);
            if (status & 0x01) != 0 {
                // PHY is ready
                return Ok(());
            }
            count += 1;
        }

        Err(DsiError::PhyNotReady)
    }

    /// Switch to video mode
    pub fn enter_video_mode(&mut self) {
        self.write_reg(DSI_MODE_CFG, DSI_MODE_CFG_VIDEO_MODE);
    }

    /// Switch to command mode
    pub fn enter_command_mode(&mut self) {
        self.write_reg(DSI_MODE_CFG, DSI_MODE_CFG_CMD_MODE);
    }

    /// Send a short packet (DCS command with 0-2 parameters)
    pub fn send_short_packet(&mut self, data_type: u8, data0: u8, data1: u8) -> Result<(), DsiError> {
        // Wait for command FIFO to be empty
        self.wait_cmd_fifo_empty()?;

        // Write command to generic header register
        let header = (data_type as u32) | ((data0 as u32) << 8) | ((data1 as u32) << 16);
        self.write_reg(DSI_GEN_HDR, header);

        Ok(())
    }

    /// Send a long packet (DCS command with more than 2 parameters)
    pub fn send_long_packet(&mut self, data_type: u8, data: &[u8]) -> Result<(), DsiError> {
        if data.is_empty() {
            return Err(DsiError::InvalidParameter);
        }

        // Wait for command FIFO to be empty
        self.wait_cmd_fifo_empty()?;

        // Write payload data
        let mut i = 0;
        while i < data.len() {
            let mut word = 0u32;
            for j in 0..4 {
                if i + j < data.len() {
                    word |= (data[i + j] as u32) << (j * 8);
                }
            }
            self.write_reg(DSI_GEN_PLD_DATA, word);
            i += 4;
        }

        // Write command header
        let wc = data.len() as u32; // Word count
        let header = (data_type as u32) | ((wc & 0xFF) << 8) | (((wc >> 8) & 0xFF) << 16);
        self.write_reg(DSI_GEN_HDR, header);

        Ok(())
    }

    /// Wait for command FIFO to be empty
    fn wait_cmd_fifo_empty(&self) -> Result<(), DsiError> {
        let timeout = 10000;
        let mut count = 0;

        while count < timeout {
            let status = self.read_reg(DSI_CMD_PKT_STATUS);
            if (status & 0x01) == 0 {
                // Command FIFO is empty
                return Ok(());
            }
            count += 1;
        }

        Err(DsiError::Timeout)
    }

    /// Enable DSI output
    pub fn enable(&mut self) {
        self.write_reg(DSI_PWR_UP, DSI_PWR_UP_POWERUP);
    }

    /// Disable DSI output
    pub fn disable(&mut self) {
        self.write_reg(DSI_PWR_UP, DSI_PWR_UP_RESET);
    }
}

/// Global DSI instances
static mut DSI_INSTANCES: [Option<Rk3588MipiDsi>; 2] = [None, None];

/// Get or initialize a DSI controller instance
pub fn get_dsi(
    controller: DsiController,
    phy_config: DsiPhyConfig,
    color_format: DsiColorFormat,
) -> &'static mut Rk3588MipiDsi {
    let idx = match controller {
        DsiController::DSI0 => 0,
        DsiController::DSI1 => 1,
    };

    unsafe {
        if DSI_INSTANCES[idx].is_none() {
            DSI_INSTANCES[idx] = Some(Rk3588MipiDsi::new(controller, phy_config, color_format));
        }
        DSI_INSTANCES[idx].as_mut().unwrap()
    }
}

